#ifdef PBC_SCOPE_INFO_NEW

#if V8_MAJOR_VERSION == 11
ScopeType ToV8ScopeType(portable::ScopeType type) {
  switch (type) {
    case kScriptScope: {
      return SCRIPT_SCOPE;
    }
    case kFunctionScope: {
      return FUNCTION_SCOPE;
    }
    case kBlockScope: {
      return BLOCK_SCOPE;
    }
    case kCatchScope: {
      return CATCH_SCOPE;
    }
    case kWithScope: {
      return WITH_SCOPE;
    }
    case kEvalScope: {
      return EVAL_SCOPE;
    }
    case kClassScope: {
      return CLASS_SCOPE;
    }
    case kModuleScope: {
      return MODULE_SCOPE;
    }
    case kReplModeScope: {
      FATAL("ReplModeScope not supported");
    }
    case kShadowRealmScope: {
      return SHADOW_REALM_SCOPE;
    }
  }
}

template <typename IsolateT>
Handle<ScopeInfo> ScopeInfo::Create(IsolateT* isolate, Zone* zone,
                                    portable::PbcScope pbc_scope,
                                    const portable::PbcScript& pbc_script,
                                    MaybeHandle<ScopeInfo> outer_scope) {
  ScopeType scope_type = ToV8ScopeType(pbc_scope.GetScopeType());
  bool is_script_scope = scope_type == SCRIPT_SCOPE;
  bool is_function_scope = scope_type == FUNCTION_SCOPE;
  bool is_eval_scope = scope_type == EVAL_SCOPE;
  bool is_module_scope = scope_type == MODULE_SCOPE;

  // TODO: support more scope.
  DCHECK(is_script_scope || is_function_scope);
  uint32_t context_local_count = pbc_scope.NumberOfLocalVars();
  bool is_declaration_scope = true;

  std::vector<Handle<String> > context_local_string;
  for (uint32_t i = 0; i < context_local_count; ++i) {
    portable::PbcLocalVar var = pbc_scope.GetLocalVar(i, pbc_script);
    context_local_string.push_back(
        UtilsInternalizeStringRef(pbc_script, *isolate, var.GetName()));
  }

  // Determine use and location of the "this" binding if it is present.
  VariableAllocationInfo receiver_info;
  if (is_script_scope) {
    receiver_info = VariableAllocationInfo::NONE;
  } else {
    receiver_info = VariableAllocationInfo::UNUSED;
  }

  // Determine use and location of the function variable if it is present.
  VariableAllocationInfo function_name_info;
  if (is_function_scope || is_module_scope || is_script_scope ||
      is_eval_scope) {
    // Always reserve space for the debug name in the scope info.
    function_name_info = VariableAllocationInfo::UNUSED;
  } else {
    function_name_info = VariableAllocationInfo::NONE;
  }
  const bool has_function_name =
      function_name_info != VariableAllocationInfo::NONE;
  const bool has_position_info = NeedsPositionInfo(scope_type);

  Handle<String> context_function_name;
  portable::PbcFunction func = pbc_scope.GetFunction(pbc_script);
  if (has_function_name &&
      function_name_info != VariableAllocationInfo::CONTEXT) {
    context_function_name =
        UtilsInternalizeStringRef(pbc_script, *isolate, func.GetName());
  }

  // If the number of locals is small, we inline directly
  // in the scope info object.
  bool has_inlined_local_names =
      context_local_count < kScopeInfoMaxInlinedLocalNamesSize;

  const bool has_new_target = false;
  // TODO(cbruni): Don't always waste a field for the inferred name.
  const bool has_inferred_function_name = is_function_scope;
  const bool has_brand = false;
  const bool should_save_class_variable_index = false;

  const uint16_t parameter_count = func.GetParameterCount();
  const bool has_outer_scope_info = !outer_scope.is_null();

  Handle<SourceTextModuleInfo> module_info;

  // Make sure the Fields enum agrees with Torque-generated offsets.
  static_assert(OffsetOfElementAt(kFlags) == kFlagsOffset);
  static_assert(OffsetOfElementAt(kParameterCount) == kParameterCountOffset);
  static_assert(OffsetOfElementAt(kContextLocalCount) ==
                kContextLocalCountOffset);

  const int local_names_container_size =
      has_inlined_local_names ? context_local_count : 1;

#if V8_MAJOR_VERSION == 11
  const int length = kVariablePartIndex + local_names_container_size +
                     context_local_count +
                     (should_save_class_variable_index ? 1 : 0) +
                     (has_function_name ? kFunctionNameEntries : 0) +
                     (has_inferred_function_name ? 1 : 0) +
                     (has_position_info ? kPositionInfoEntries : 0) +
                     (has_outer_scope_info ? 1 : 0);
#else
  const int length =
      kVariablePartIndex + local_names_container_size + context_local_count +
      (should_save_class_variable_index ? 1 : 0) +
      (has_function_name ? kFunctionNameEntries : 0) +
      (has_inferred_function_name ? 1 : 0) + (has_outer_scope_info ? 1 : 0);
#endif

  // Create hash table if local names are not inlined.
  Handle<NameToIndexHashTable> local_names_hashtable;
  if (!has_inlined_local_names) {
    local_names_hashtable = NameToIndexHashTable::New(
        isolate, context_local_count, AllocationType::kOld);
  }

  Handle<ScopeInfo> scope_info_handle =
      isolate->factory()->NewScopeInfo(length);
  int index = kVariablePartIndex;
  {
    DisallowGarbageCollection no_gc;
#if V8_MAJOR_VERSION == 11
    Handle<ScopeInfo> scope_info = scope_info_handle;
#else
    Tagged<ScopeInfo> scope_info = *scope_info_handle;
#endif
    WriteBarrierMode mode = scope_info->GetWriteBarrierMode(no_gc);

    bool has_simple_parameters = false;
    bool is_asm_module = false;
    bool sloppy_eval_can_extend_vars = false;
    if (is_function_scope) {
      has_simple_parameters = true;
    }
    FunctionKind function_kind = FunctionKind::kNormalFunction;
    if (is_declaration_scope) {
      function_kind = ConvertFunctionKind(func.GetFunctionKind());
    }
    bool is_debug_evaluate_scope_ = false;
    bool force_context_for_language_mode = false;
    if (!is_function_scope && !is_script_scope && has_outer_scope_info) {
      force_context_for_language_mode =
          pbc_scope.IsStrictMode() &&
          outer_scope.ToHandleChecked()->language_mode() ==
              LanguageMode::kSloppy;
    }
    bool private_name_lookup_skips_outer_class = false;

#if V8_MAJOR_VERSION == 11
    bool is_repl_mode_scope = false;
#else
    bool is_hidden = false;
    bool is_wrapped_function = func.GetFunctionSyntaxKind() ==
                               portable::PbcFunctionSyntaxKind::kWrapped;
#endif
    bool has_context_extension_slot = is_script_scope;

    LanguageMode language_mode = pbc_scope.IsStrictMode()
                                     ? LanguageMode::kStrict
                                     : LanguageMode::kSloppy;

// Encode the flags.
#if V8_MAJOR_VERSION == 11
    uint32_t flags =
        ScopeTypeBits::encode(scope_type) |
        SloppyEvalCanExtendVarsBit::encode(sloppy_eval_can_extend_vars) |
        LanguageModeBit::encode(language_mode) |
        DeclarationScopeBit::encode(is_declaration_scope) |
        ReceiverVariableBits::encode(receiver_info) |
        ClassScopeHasPrivateBrandBit::encode(has_brand) |
        HasSavedClassVariableBit::encode(should_save_class_variable_index) |
        HasNewTargetBit::encode(has_new_target) |
        FunctionVariableBits::encode(function_name_info) |
        HasInferredFunctionNameBit::encode(has_inferred_function_name) |
        IsAsmModuleBit::encode(is_asm_module) |
        HasSimpleParametersBit::encode(has_simple_parameters) |
        FunctionKindBits::encode(function_kind) |
        HasOuterScopeInfoBit::encode(has_outer_scope_info) |
        IsDebugEvaluateScopeBit::encode(is_debug_evaluate_scope_) |
        ForceContextAllocationBit::encode(force_context_for_language_mode) |
        PrivateNameLookupSkipsOuterClassBit::encode(
            private_name_lookup_skips_outer_class) |
        HasContextExtensionSlotBit::encode(has_context_extension_slot) |
        IsReplModeScopeBit::encode(is_repl_mode_scope) |
        HasLocalsBlockListBit::encode(false);
#else
    uint32_t flags =
        ScopeTypeBits::encode(scope_type) |
        SloppyEvalCanExtendVarsBit::encode(sloppy_eval_can_extend_vars) |
        LanguageModeBit::encode(language_mode) |
        DeclarationScopeBit::encode(is_declaration_scope) |
        ReceiverVariableBits::encode(receiver_info) |
        ClassScopeHasPrivateBrandBit::encode(has_brand) |
        HasSavedClassVariableBit::encode(should_save_class_variable_index) |
        HasNewTargetBit::encode(has_new_target) |
        FunctionVariableBits::encode(function_name_info) |
        HasInferredFunctionNameBit::encode(has_inferred_function_name) |
        IsAsmModuleBit::encode(is_asm_module) |
        HasSimpleParametersBit::encode(has_simple_parameters) |
        FunctionKindBits::encode(function_kind) |
        HasOuterScopeInfoBit::encode(has_outer_scope_info) |
        IsDebugEvaluateScopeBit::encode(is_debug_evaluate_scope_) |
        ForceContextAllocationBit::encode(force_context_for_language_mode) |
        PrivateNameLookupSkipsOuterClassBit::encode(
            private_name_lookup_skips_outer_class) |
        HasContextExtensionSlotBit::encode(has_context_extension_slot) |
        IsHiddenBit::encode(is_hidden) |
        IsWrappedFunctionBit::encode(is_wrapped_function);
#endif
    scope_info->set_flags(flags);

    scope_info->set_parameter_count(parameter_count);
    scope_info->set_context_local_count(context_local_count);

    scope_info->set_position_info_start(0);
    scope_info->set_position_info_end(0);

    if (!has_inlined_local_names) {
      scope_info->set_context_local_names_hashtable(*local_names_hashtable);
    }

    // Add context locals' names and info, module variables' names and info.
    // Context locals are added using their index.
    int context_local_base = index;
    int context_local_info_base =
        context_local_base + local_names_container_size;

    for (uint32_t i = 0; i < context_local_count; ++i) {
      // Due to duplicate parameters, context locals aren't guaranteed to
      // come in order.
      VariableMode variable_mode = VariableMode::kConst;
      InitializationFlag init_flag = InitializationFlag::kCreatedInitialized;
      MaybeAssignedFlag maybe_assigned = MaybeAssignedFlag::kNotAssigned;
      IsStaticFlag is_static = IsStaticFlag::kNotStatic;

      uint32_t info = VariableModeBits::encode(variable_mode) |
                      InitFlagBit::encode(init_flag) |
                      MaybeAssignedFlagBit::encode(maybe_assigned) |
                      ParameterNumberBits::encode(ParameterNumberBits::kMax) |
                      IsStaticFlagBit::encode(is_static);

      if (has_inlined_local_names) {
        scope_info->set(context_local_base + i, *context_local_string[i], mode);
      } else {
        Handle<NameToIndexHashTable> new_table = NameToIndexHashTable::Add(
            isolate, local_names_hashtable, context_local_string[i], i);
        DCHECK_EQ(*new_table, *local_names_hashtable);
        USE(new_table);
      }
      scope_info->set(context_local_info_base + i, Smi::FromInt(info));
    }

    // Advance past local names and local names info.
    index += local_names_container_size + context_local_count;

    // If present, add the function variable name and its index.
    DCHECK_EQ(index, scope_info->FunctionVariableInfoIndex());
    if (has_function_name &&
        function_name_info != VariableAllocationInfo::CONTEXT) {
      int var_index = scope_info->ContextLength() - 1;
      scope_info->set(index++, *context_function_name, mode);
      scope_info->set(index++, Smi::FromInt(var_index));
    }

    DCHECK_EQ(index, scope_info->InferredFunctionNameIndex());
    if (has_inferred_function_name) {
      // The inferred function name is taken from the SFI.
      index++;
    }

#if V8_MAJOR_VERSION == 11
    DCHECK_EQ(index, scope_info->PositionInfoIndex());
    if (has_position_info) {
      scope_info->set(index++, Smi::FromInt(0));
      scope_info->set(index++, Smi::FromInt(0));
    }
#endif

    // If present, add the outer scope info.
    DCHECK(index == scope_info->OuterScopeInfoIndex());
    if (has_outer_scope_info) {
      scope_info->set(index++, *outer_scope.ToHandleChecked(), mode);
    }
  }

  DCHECK_EQ(index, scope_info_handle->length());
  DCHECK_EQ(parameter_count, scope_info_handle->ParameterCount());

  return scope_info_handle;
}

template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
    Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
                                        portable::PbcScope pbc_scope,
                                        const portable::PbcScript& module,
                                        MaybeHandle<ScopeInfo> outer_scope);
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
    Handle<ScopeInfo> ScopeInfo::Create(LocalIsolate* isolate, Zone* zone,
                                        portable::PbcScope pbc_scope,
                                        const portable::PbcScript& module,
                                        MaybeHandle<ScopeInfo> outer_scope);

#else

ScopeType ToV8ScopeType(portable::ScopeType type) {
  switch (type) {
    case kScriptScope: {
      return SCRIPT_SCOPE;
    }
    case kFunctionScope: {
      return FUNCTION_SCOPE;
    }
    case kBlockScope: {
      return BLOCK_SCOPE;
    }
    case kCatchScope: {
      return CATCH_SCOPE;
    }
    case kWithScope: {
      return WITH_SCOPE;
    }
    case kEvalScope: {
      return EVAL_SCOPE;
    }
    case kClassScope: {
      return CLASS_SCOPE;
    }
    case kModuleScope: {
      return MODULE_SCOPE;
    }
    case kReplModeScope: {
      return REPL_MODE_SCOPE;
    }
    case kShadowRealmScope: {
      return SHADOW_REALM_SCOPE;
    }
  }
}

// static
template <typename IsolateT>
Handle<ScopeInfo> ScopeInfo::Create(IsolateT* isolate, Zone* zone,
                                    portable::PbcScope pbc_scope,
                                    const portable::PbcScript& pbc_script,
                                    MaybeHandle<ScopeInfo> outer_scope) {
  ScopeType scope_type = ToV8ScopeType(pbc_scope.GetScopeType());
  bool is_script_scope = scope_type == SCRIPT_SCOPE;
  bool is_function_scope = scope_type == FUNCTION_SCOPE;
  bool is_eval_scope = scope_type == EVAL_SCOPE;
  bool is_module_scope = scope_type == MODULE_SCOPE;

  // TODO: support more scope.
  DCHECK(is_script_scope || is_function_scope);
  uint32_t context_local_count = pbc_scope.NumberOfLocalVars();
  bool is_declaration_scope = true;

  std::vector<Handle<String> > context_local_string;
  for (uint32_t i = 0; i < context_local_count; ++i) {
    portable::PbcLocalVar var = pbc_scope.GetLocalVar(i, pbc_script);
    context_local_string.push_back(
        UtilsInternalizeStringRef(pbc_script, *isolate, var.GetName()));
  }

  // Determine use and location of the "this" binding if it is present.
  VariableAllocationInfo receiver_info;
  if (is_script_scope) {
    receiver_info = VariableAllocationInfo::NONE;
  } else {
    receiver_info = VariableAllocationInfo::UNUSED;
  }

  // Determine use and location of the function variable if it is present.
  VariableAllocationInfo function_name_info;
  if (is_function_scope || is_module_scope || is_script_scope ||
      is_eval_scope) {
    // Always reserve space for the debug name in the scope info.
    function_name_info = VariableAllocationInfo::UNUSED;
  } else {
    function_name_info = VariableAllocationInfo::NONE;
  }
  const bool has_function_name =
      function_name_info != VariableAllocationInfo::NONE;
  Handle<String> context_function_name;
  portable::PbcFunction func = pbc_scope.GetFunction(pbc_script);
  if (has_function_name &&
      function_name_info != VariableAllocationInfo::CONTEXT) {
    context_function_name =
        UtilsInternalizeStringRef(pbc_script, *isolate, func.GetName());
  }

  // If the number of locals is small, we inline directly
  // in the scope info object.
  bool has_inlined_local_names =
      context_local_count < kScopeInfoMaxInlinedLocalNamesSize;

  const bool has_new_target = false;
  // TODO(cbruni): Don't always waste a field for the inferred name.
  const bool has_inferred_function_name = is_function_scope;
  const bool has_brand = false;
  const bool should_save_class_variable_index = false;

  const uint16_t parameter_count = func.GetParameterCount();
  const bool has_outer_scope_info = !outer_scope.is_null();

  Handle<SourceTextModuleInfo> module_info;

  // Make sure the Fields enum agrees with Torque-generated offsets.
  static_assert(OffsetOfElementAt(kFlags) == kFlagsOffset);
  static_assert(OffsetOfElementAt(kParameterCount) == kParameterCountOffset);
  static_assert(OffsetOfElementAt(kContextLocalCount) ==
                kContextLocalCountOffset);

  const int local_names_container_size =
      has_inlined_local_names ? context_local_count : 1;

  const int length =
      kVariablePartIndex + local_names_container_size + context_local_count +
      (should_save_class_variable_index ? 1 : 0) +
      (has_function_name ? kFunctionNameEntries : 0) +
      (has_inferred_function_name ? 1 : 0) + (has_outer_scope_info ? 1 : 0);

  // Create hash table if local names are not inlined.
  Handle<NameToIndexHashTable> local_names_hashtable;
  if (!has_inlined_local_names) {
    local_names_hashtable = NameToIndexHashTable::New(
        isolate, context_local_count, AllocationType::kOld);
  }

  Handle<ScopeInfo> scope_info_handle =
      isolate->factory()->NewScopeInfo(length);
  int index = kVariablePartIndex;
  {
    DisallowGarbageCollection no_gc;
    Tagged<ScopeInfo> scope_info = *scope_info_handle;
    WriteBarrierMode mode = scope_info->GetWriteBarrierMode(no_gc);

    bool has_simple_parameters = false;
    bool is_asm_module = false;
    bool sloppy_eval_can_extend_vars = false;
    if (is_function_scope) {
      has_simple_parameters = true;
    }
    FunctionKind function_kind = FunctionKind::kNormalFunction;
    if (is_declaration_scope) {
      function_kind = ConvertFunctionKind(func.GetFunctionKind());
    }
    bool is_debug_evaluate_scope_ = false;
    bool force_context_for_language_mode = false;
    if (!is_function_scope && !is_script_scope && has_outer_scope_info) {
      force_context_for_language_mode =
          pbc_scope.IsStrictMode() &&
          outer_scope.ToHandleChecked()->language_mode() ==
              LanguageMode::kSloppy;
    }
    bool private_name_lookup_skips_outer_class = false;

    bool is_hidden = false;
    bool is_wrapped_function = func.GetFunctionSyntaxKind() ==
                               portable::PbcFunctionSyntaxKind::kWrapped;

    bool has_context_extension_slot = is_script_scope;

    LanguageMode language_mode = pbc_scope.IsStrictMode()
                                     ? LanguageMode::kStrict
                                     : LanguageMode::kSloppy;

    // Encode the flags.
    uint32_t flags =
        ScopeTypeBits::encode(scope_type) |
        SloppyEvalCanExtendVarsBit::encode(sloppy_eval_can_extend_vars) |
        LanguageModeBit::encode(language_mode) |
        DeclarationScopeBit::encode(is_declaration_scope) |
        ReceiverVariableBits::encode(receiver_info) |
        ClassScopeHasPrivateBrandBit::encode(has_brand) |
        HasSavedClassVariableBit::encode(should_save_class_variable_index) |
        HasNewTargetBit::encode(has_new_target) |
        FunctionVariableBits::encode(function_name_info) |
        HasInferredFunctionNameBit::encode(has_inferred_function_name) |
        IsAsmModuleBit::encode(is_asm_module) |
        HasSimpleParametersBit::encode(has_simple_parameters) |
        FunctionKindBits::encode(function_kind) |
        HasOuterScopeInfoBit::encode(has_outer_scope_info) |
        IsDebugEvaluateScopeBit::encode(is_debug_evaluate_scope_) |
        ForceContextAllocationBit::encode(force_context_for_language_mode) |
        PrivateNameLookupSkipsOuterClassBit::encode(
            private_name_lookup_skips_outer_class) |
        HasContextExtensionSlotBit::encode(has_context_extension_slot) |
        IsHiddenBit::encode(is_hidden) |
        IsWrappedFunctionBit::encode(is_wrapped_function);
    scope_info->set_flags(flags);

    scope_info->set_parameter_count(parameter_count);
    scope_info->set_context_local_count(context_local_count);

    scope_info->set_position_info_start(0);
    scope_info->set_position_info_end(0);

    if (!has_inlined_local_names) {
      scope_info->set_context_local_names_hashtable(*local_names_hashtable);
    }

    // Add context locals' names and info, module variables' names and info.
    // Context locals are added using their index.
    int context_local_base = index;
    int context_local_info_base =
        context_local_base + local_names_container_size;

    for (uint32_t i = 0; i < context_local_count; ++i) {
      // Due to duplicate parameters, context locals aren't guaranteed to
      // come in order.
      VariableMode variable_mode = VariableMode::kConst;
      InitializationFlag init_flag = InitializationFlag::kCreatedInitialized;
      MaybeAssignedFlag maybe_assigned = MaybeAssignedFlag::kNotAssigned;
      IsStaticFlag is_static = IsStaticFlag::kNotStatic;

      uint32_t info = VariableModeBits::encode(variable_mode) |
                      InitFlagBit::encode(init_flag) |
                      MaybeAssignedFlagBit::encode(maybe_assigned) |
                      ParameterNumberBits::encode(ParameterNumberBits::kMax) |
                      IsStaticFlagBit::encode(is_static);

      if (has_inlined_local_names) {
        scope_info->set(context_local_base + i, *context_local_string[i], mode);
      } else {
        Handle<NameToIndexHashTable> new_table = NameToIndexHashTable::Add(
            isolate, local_names_hashtable, context_local_string[i], i);
        DCHECK_EQ(*new_table, *local_names_hashtable);
        USE(new_table);
      }
      scope_info->set(context_local_info_base + i, Smi::FromInt(info));
    }

    // Advance past local names and local names info.
    index += local_names_container_size + context_local_count;

    // If present, add the function variable name and its index.
    DCHECK_EQ(index, scope_info->FunctionVariableInfoIndex());
    if (has_function_name &&
        function_name_info != VariableAllocationInfo::CONTEXT) {
      int var_index = scope_info->ContextLength() - 1;
      scope_info->set(index++, *context_function_name, mode);
      scope_info->set(index++, Smi::FromInt(var_index));
    }

    DCHECK_EQ(index, scope_info->InferredFunctionNameIndex());
    if (has_inferred_function_name) {
      // The inferred function name is taken from the SFI.
      index++;
    }

    // If present, add the outer scope info.
    DCHECK(index == scope_info->OuterScopeInfoIndex());
    if (has_outer_scope_info) {
      scope_info->set(index++, *outer_scope.ToHandleChecked(), mode);
    }
  }

  DCHECK_EQ(index, scope_info_handle->length());
  DCHECK_EQ(parameter_count, scope_info_handle->ParameterCount());

  return scope_info_handle;
}

template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
    Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
                                        portable::PbcScope pbc_scope,
                                        const portable::PbcScript& module,
                                        MaybeHandle<ScopeInfo> outer_scope);
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
    Handle<ScopeInfo> ScopeInfo::Create(LocalIsolate* isolate, Zone* zone,
                                        portable::PbcScope pbc_scope,
                                        const portable::PbcScript& module,
                                        MaybeHandle<ScopeInfo> outer_scope);
#endif
#endif